import base64
import hmac
import hashlib
import json
import time
import httpx
import asyncio
from typing import Dict, Optional, Any
from nacl.signing import SigningKey
import base64


class BackpackExchange:
    def __init__(self, api_key: str = "", api_secret: str = "", testnet: bool = False):
        """
        初始化Backpack交易所API客户端
        
        Args:
            api_key: API密钥
            api_secret: API密钥对应的私钥（base64编码的ED25519私钥）
            testnet: 是否使用测试网络
        """
        self.api_key = api_key
        self.api_secret = api_secret
        
        # 从base64字符串加载ED25519私钥
        if api_secret:
            try:
                # 移除可能存在的padding
                api_secret = api_secret.rstrip('=')
                # 计算需要的padding
                padding = 4 - (len(api_secret) % 4)
                if padding != 4:
                    api_secret += '=' * padding
                # 解码base64
                decoded_secret = base64.b64decode(api_secret)
                self.signing_key = SigningKey(decoded_secret)
            except Exception as e:
                print(f"初始化签名密钥时出错: {str(e)}")
                self.signing_key = None
        else:
            self.signing_key = None
            
        # API基础URL和WebSocket URL
        self.base_url = "https://api.backpack.exchange"
        self.ws_url = "wss://ws.backpack.exchange"
        
        self.default_window = 5000  # 默认时间窗口5秒
    
    async def _get_signature(self, instruction_type: str, params: Dict[str, Any], timestamp: int, window: int) -> str:
        """生成API请求签名"""
        # 按字母顺序排序参数
        sorted_params = {k: params[k] for k in sorted(params.keys())}
        
        # 构建查询字符串
        query_string = '&'.join([f"{k}={v}" for k, v in sorted_params.items()])
        
        # 添加指令类型、时间戳和窗口
        if query_string:
            message = f"instruction={instruction_type}&{query_string}&timestamp={timestamp}&window={window}"
        else:
            message = f"instruction={instruction_type}&timestamp={timestamp}&window={window}"
        
        # 使用ED25519私钥签名
        signature = self.signing_key.sign(message.encode())
        
        # 返回base64编码的签名
        return base64.b64encode(signature.signature).decode()
    
    async def _request(self, method: str, endpoint: str, instruction_type: str, 
                     params: Optional[Dict[str, Any]] = None, 
                     need_signature: bool = True) -> Dict:
        """发送API请求"""
        url = f"{self.base_url}{endpoint}"
        
        # 准备请求头
        headers = {
            "Content-Type": "application/json"
        }
        
        # 创建查询参数
        query_params = {}
        
        # 添加instruction参数
        query_params["instruction"] = instruction_type
        
        # 将所有参数添加到查询参数中
        if params:
            for key, value in params.items():
                query_params[key] = value
        
        # 如果需要签名，添加认证头
        if need_signature and self.signing_key:
            timestamp = int(time.time() * 1000)  # 毫秒级时间戳
            window = self.default_window
            
            # 获取签名
            signature = await self._get_signature(
                instruction_type=instruction_type,
                params=params or {},
                timestamp=timestamp,
                window=window
            )
            
            # 添加认证头
            headers.update({
                "X-API-Key": self.api_key,
                "X-Timestamp": str(timestamp),
                "X-Window": str(window),
                "X-Signature": signature
            })
        
        # 打印请求信息进行调试
        print(f"请求URL: {url}")
        print(f"请求方法: {method}")
        print(f"请求参数: {json.dumps(query_params, indent=2)}")
        print(f"请求头: {json.dumps(headers, indent=2)}")
        
        async with httpx.AsyncClient() as client:
            if method.upper() == "GET":
                # 对于GET请求，将参数转换为URL查询参数
                response = await client.get(url, params=query_params, headers=headers)
            elif method.upper() == "POST":
                # 根据客服建议，POST请求也应使用URL参数，而非请求体
                response = await client.post(url, params=query_params, headers=headers)
            elif method.upper() == "DELETE":
                # DELETE请求同样使用URL参数
                response = await client.delete(url, params=query_params, headers=headers)
            else:
                # 其他请求类型
                response = await client.request(method, url, params=query_params, headers=headers)
            
            # 检查响应状态和内容
            try:
                response.raise_for_status()
                print(f"响应状态码: {response.status_code}")
                print(f"响应内容: {response.text}")
                return response.json()
            except httpx.HTTPStatusError as e:
                print(f"HTTP错误: {e.response.status_code}")
                print(f"响应内容: {e.response.text}")
                print(f"请求URL: {url}")
                print(f"请求头: {headers}")
                raise
    
    # 创建现货限价单
    async def create_spot_limit_order(self, symbol: str, side: str, 
                                    price: str, quantity: str, 
                                    client_id: Optional[int] = None,
                                    post_only: bool = False,
                                    quote_quantity: Optional[str] = None,
                                    self_trade_prevention: Optional[str] = None,
                                    auto_borrow: Optional[bool] = None,
                                    auto_borrow_repay: Optional[bool] = None,
                                    auto_lend: Optional[bool] = None,
                                    auto_lend_redeem: Optional[bool] = None) -> Dict:
        """
        创建现货限价单
        
        Args:
            symbol: 交易对，例如'BTC_USDT'
            side: 交易方向，'Bid'(买入)或'Ask'(卖出)
            price: 价格
            quantity: 数量
            client_id: 客户端订单ID（可选，必须是整数）
            post_only: 是否仅做挂单方（可选）
            quote_quantity: 计价货币数量（可选）
            self_trade_prevention: 自成交预防模式，例如'RejectTaker'（可选）
            auto_borrow: 是否自动借款（可选）
            auto_borrow_repay: 是否自动借款并还款（可选）
            auto_lend: 是否自动出借（可选）
            auto_lend_redeem: 是否自动出借并赎回（可选）

        Returns:
            订单响应
        """
        endpoint = "/api/v1/order"  # 根据官方文档，正确的端点是单数形式
        instruction_type = "orderExecute"
        
        # 转换交易方向格式
        if side.upper() == "BUY":
            side = "Bid"
        elif side.upper() == "SELL":
            side = "Ask"
        
        # 构建订单参数，确保类型符合API要求
        params = {
            "symbol": symbol,
            "side": side,  # 使用Bid或Ask
            "orderType": "Limit",   # 使用orderType而不是type，值为Limit而不是LIMIT
            "price": price,
            "quantity": quantity,
            "timeInForce": "POST_ONLY" if post_only else "GTC"
        }
        
        # 如果设置了post_only参数
        if post_only:
            params["postOnly"] = True
        
        # 添加可选参数
        if client_id:
            params["clientId"] = client_id
        
        if quote_quantity:
            params["quoteQuantity"] = quote_quantity
            
        if self_trade_prevention:
            params["selfTradePrevention"] = self_trade_prevention
            
        if auto_borrow is not None:
            params["autoBorrow"] = auto_borrow
            
        if auto_borrow_repay is not None:
            params["autoBorrowRepay"] = auto_borrow_repay
            
        if auto_lend is not None:
            params["autoLend"] = auto_lend
            
        if auto_lend_redeem is not None:
            params["autoLendRedeem"] = auto_lend_redeem
            
        return await self._request("POST", endpoint, instruction_type, params)
    
    # 创建现货市价单
    async def create_spot_market_order(self, symbol: str, side: str, 
                                      quantity: str = None,
                                      quote_quantity: str = None,
                                      client_id: Optional[int] = None,
                                      self_trade_prevention: Optional[str] = None,
                                      auto_borrow: Optional[bool] = None,
                                      auto_borrow_repay: Optional[bool] = None,
                                      auto_lend: Optional[bool] = None,
                                      auto_lend_redeem: Optional[bool] = None) -> Dict:
        """
        创建现货市价单
        
        Args:
            symbol: 交易对，例如'BTC_USDT'
            side: 交易方向，'Bid'(买入)或'Ask'(卖出)
            quantity: 数量（quantity和quote_quantity必须提供一个）
            quote_quantity: 计价货币数量，例如用多少USDT买入（可选）
            client_id: 客户端订单ID（可选，必须是整数）
            self_trade_prevention: 自成交预防模式，例如'RejectTaker'（可选）
            auto_borrow: 是否自动借款（可选）
            auto_borrow_repay: 是否自动借款并还款（可选）
            auto_lend: 是否自动出借（可选）
            auto_lend_redeem: 是否自动出借并赎回（可选）

        Returns:
            订单响应
        """
        if not quantity and not quote_quantity:
            raise ValueError("quantity或quote_quantity必须提供一个")
            
        endpoint = "/api/v1/order"  # 根据官方文档，正确的端点是单数形式
        instruction_type = "orderExecute"
        
        # 转换交易方向格式
        if side.upper() == "BUY":
            side = "Bid"
        elif side.upper() == "SELL":
            side = "Ask"
        
        params = {
            "symbol": symbol,
            "side": side,  # 使用Bid或Ask
            "orderType": "Market",  # 使用orderType而不是type，值为Market而不是MARKET
        }
        
        # 添加数量参数（二选一）
        if quantity:
            params["quantity"] = quantity
        if quote_quantity:
            params["quoteQuantity"] = quote_quantity
        
        # 添加可选参数
        if client_id:
            params["clientId"] = client_id
        
        if self_trade_prevention:
            params["selfTradePrevention"] = self_trade_prevention
            
        if auto_borrow is not None:
            params["autoBorrow"] = auto_borrow
            
        if auto_borrow_repay is not None:
            params["autoBorrowRepay"] = auto_borrow_repay
            
        if auto_lend is not None:
            params["autoLend"] = auto_lend
            
        if auto_lend_redeem is not None:
            params["autoLendRedeem"] = auto_lend_redeem
            
        return await self._request("POST", endpoint, instruction_type, params)
    
    # 创建永续合约限价单
    async def create_perp_limit_order(self, symbol: str, side: str, 
                                    price: str, quantity: str, 
                                    client_id: str = None,
                                    post_only: bool = False,
                                    reduce_only: bool = False,
                                    quote_quantity: Optional[str] = None,
                                    self_trade_prevention: Optional[str] = None,
                                    take_profit_trigger_price: Optional[str] = None,
                                    take_profit_limit_price: Optional[str] = None,
                                    take_profit_trigger_by: Optional[str] = None,
                                    stop_loss_trigger_price: Optional[str] = None,
                                    stop_loss_limit_price: Optional[str] = None,
                                    stop_loss_trigger_by: Optional[str] = None,
                                    trigger_price: Optional[str] = None,
                                    trigger_by: Optional[str] = None,
                                    trigger_quantity: Optional[str] = None) -> Dict:
        """
        创建永续合约限价单
        
        Args:
            symbol: 交易对，例如'BTC_USDT_PERP'
            side: 交易方向，'Bid'(买入)或'Ask'(卖出)
            price: 价格
            quantity: 数量
            client_id: 客户端订单ID（可选）
            post_only: 是否仅做挂单方（可选）
            reduce_only: 是否仅减仓（可选）
            quote_quantity: 计价货币数量（可选）
            self_trade_prevention: 自成交预防模式，例如'RejectTaker'（可选）
            take_profit_trigger_price: 止盈触发价格（可选）
            take_profit_limit_price: 止盈限价（可选）
            take_profit_trigger_by: 止盈触发方式（可选）
            stop_loss_trigger_price: 止损触发价格（可选）
            stop_loss_limit_price: 止损限价（可选）
            stop_loss_trigger_by: 止损触发方式（可选）
            trigger_price: 触发价格，用于条件单（可选）
            trigger_by: 触发方式（可选）
            trigger_quantity: 触发数量（可选）

        Returns:
            订单响应
        """
        endpoint = "/api/v1/order"  # 根据官方文档，正确的端点是单数形式
        instruction_type = "orderExecute"
        
        # 转换交易方向格式
        if side.upper() == "BUY":
            side = "Bid"
        elif side.upper() == "SELL":
            side = "Ask"
        
        params = {
            "symbol": symbol,
            "side": side,  # 使用Bid或Ask
            "orderType": "Limit",  # 使用orderType而不是type，值为Limit而不是LIMIT
            "price": price,
            "quantity": quantity,
            "timeInForce": "POST_ONLY" if post_only else "GTC"
        }
        
        # 如果设置了post_only参数
        if post_only:
            params["postOnly"] = True
        
        # 添加可选参数
        if client_id:
            params["clientId"] = client_id
        
        if reduce_only:
            params["reduceOnly"] = True
            
        if quote_quantity:
            params["quoteQuantity"] = quote_quantity
            
        if self_trade_prevention:
            params["selfTradePrevention"] = self_trade_prevention

        # 止盈止损参数（新格式）
        if take_profit_trigger_price:
            params["takeProfitTriggerPrice"] = take_profit_trigger_price
            
        if take_profit_limit_price:
            params["takeProfitLimitPrice"] = take_profit_limit_price
            
        if take_profit_trigger_by:
            params["takeProfitTriggerBy"] = take_profit_trigger_by
            
        if stop_loss_trigger_price:
            params["stopLossTriggerPrice"] = stop_loss_trigger_price
            
        if stop_loss_limit_price:
            params["stopLossLimitPrice"] = stop_loss_limit_price
            
        if stop_loss_trigger_by:
            params["stopLossTriggerBy"] = stop_loss_trigger_by
            
        # 条件单参数
        if trigger_price:
            params["triggerPrice"] = trigger_price
            
        if trigger_by:
            params["triggerBy"] = trigger_by
            
        if trigger_quantity:
            params["triggerQuantity"] = trigger_quantity
            
        return await self._request("POST", endpoint, instruction_type, params)
    
    # 创建永续合约市价单
    async def create_perp_market_order(self, symbol: str, side: str, 
                                     quantity: str = None,
                                     quote_quantity: str = None,
                                     client_id: str = None,
                                     reduce_only: bool = False,
                                     self_trade_prevention: Optional[str] = None,
                                     take_profit_trigger_price: Optional[str] = None,
                                     take_profit_limit_price: Optional[str] = None,
                                     take_profit_trigger_by: Optional[str] = None,
                                     stop_loss_trigger_price: Optional[str] = None,
                                     stop_loss_limit_price: Optional[str] = None,
                                     stop_loss_trigger_by: Optional[str] = None,
                                     trigger_price: Optional[str] = None,
                                     trigger_by: Optional[str] = None,
                                     trigger_quantity: Optional[str] = None) -> Dict:
        """
        创建永续合约市价单
        
        Args:
            symbol: 交易对，例如'BTC_USDT_PERP'
            side: 交易方向，'Bid'(买入)或'Ask'(卖出)
            quantity: 数量（quantity和quote_quantity必须提供一个）
            quote_quantity: 计价货币数量（可选）
            client_id: 客户端订单ID（可选）
            reduce_only: 是否仅减仓（可选）
            self_trade_prevention: 自成交预防模式，例如'RejectTaker'（可选）
            take_profit_trigger_price: 止盈触发价格（可选）
            take_profit_limit_price: 止盈限价（可选）
            take_profit_trigger_by: 止盈触发方式（可选）
            stop_loss_trigger_price: 止损触发价格（可选）
            stop_loss_limit_price: 止损限价（可选）
            stop_loss_trigger_by: 止损触发方式（可选）
            trigger_price: 触发价格，用于条件单（可选）
            trigger_by: 触发方式（可选）
            trigger_quantity: 触发数量（可选）

        Returns:
            订单响应
        """
        if not quantity and not quote_quantity:
            raise ValueError("quantity或quote_quantity必须提供一个")
            
        endpoint = "/api/v1/order"  # 根据官方文档，正确的端点是单数形式
        instruction_type = "orderExecute"
        
        # 转换交易方向格式
        if side.upper() == "BUY":
            side = "Bid"
        elif side.upper() == "SELL":
            side = "Ask"
        
        params = {
            "symbol": symbol,
            "side": side,  # 使用Bid或Ask
            "orderType": "Market",  # 使用orderType而不是type，值为Market而不是MARKET
        }
        
        # 添加数量参数（二选一）
        if quantity:
            params["quantity"] = quantity
        if quote_quantity:
            params["quoteQuantity"] = quote_quantity
        
        # 添加可选参数
        if client_id:
            params["clientId"] = client_id
        
        if reduce_only:
            params["reduceOnly"] = True
            
        if self_trade_prevention:
            params["selfTradePrevention"] = self_trade_prevention
            
        # 止盈止损参数（新格式）
        if take_profit_trigger_price:
            params["takeProfitTriggerPrice"] = take_profit_trigger_price
            
        if take_profit_limit_price:
            params["takeProfitLimitPrice"] = take_profit_limit_price
            
        if take_profit_trigger_by:
            params["takeProfitTriggerBy"] = take_profit_trigger_by
            
        if stop_loss_trigger_price:
            params["stopLossTriggerPrice"] = stop_loss_trigger_price
            
        if stop_loss_limit_price:
            params["stopLossLimitPrice"] = stop_loss_limit_price
            
        if stop_loss_trigger_by:
            params["stopLossTriggerBy"] = stop_loss_trigger_by
            
        # 条件单参数
        if trigger_price:
            params["triggerPrice"] = trigger_price
            
        if trigger_by:
            params["triggerBy"] = trigger_by
            
        if trigger_quantity:
            params["triggerQuantity"] = trigger_quantity
            
        return await self._request("POST", endpoint, instruction_type, params)
    
    # 查询订单
    async def get_order(self, symbol: str, order_id: Optional[str] = None, 
                      client_id: Optional[str] = None) -> Dict:
        """
        查询订单状态
        
        Args:
            symbol: 交易对
            order_id: 订单ID（order_id和client_id必须提供一个）
            client_id: 客户端订单ID

        Returns:
            订单信息
        """
        if not order_id and not client_id:
            raise ValueError("order_id或client_id必须提供一个")
            
        endpoint = "/api/v1/order"
        instruction_type = "orderQuery"
        
        params = {"symbol": symbol}
        
        if order_id:
            params["orderId"] = order_id
        if client_id:
            params["clientId"] = client_id
            
        return await self._request("GET", endpoint, instruction_type, params)
    
    # 取消订单
    async def cancel_order(self, symbol: str, order_id: Optional[str] = None, 
                         client_id: Optional[str] = None) -> Dict:
        """
        取消订单
        
        Args:
            symbol: 交易对
            order_id: 订单ID（order_id和client_id必须提供一个）
            client_id: 客户端订单ID

        Returns:
            取消结果
        """
        if not order_id and not client_id:
            raise ValueError("order_id或client_id必须提供一个")
            
        endpoint = "/api/v1/order"
        instruction_type = "orderCancel"
        
        params = {"symbol": symbol}
        
        if order_id:
            params["orderId"] = order_id
        if client_id:
            params["clientId"] = client_id
            
        return await self._request("DELETE", endpoint, instruction_type, params)


# 使用示例
async def spot_order_example():
    """现货下单示例"""
    print("======== 现货下单示例 ========")
    
    # 初始化交易所API客户端（请替换为您的API密钥）
    api_key = "X1PGZL7zYyK3J0eDci7is3M5RwNlZy3iuwuQpeuylxs="
    api_secret = "jWm2aX0DCGFig0AI57D0ZDSKSy40tVOg2/ACcwbovng="
    exchange = BackpackExchange(api_key=api_key, api_secret=api_secret)
    
    try:
        # 1. 创建现货限价买单示例
        symbol = "JUP_USDC"  # 交易对
        side = "Bid"         # 买入，使用Bid
        price = "0.5233"     # 价格
        quantity = "20"      # 数量
        client_id = int(time.time())  # 使用时间戳作为整数客户端ID
        
        print(f"正在创建限价买单: {symbol}, 价格: {price}, 数量: {quantity}")
        print(f"客户端订单ID: {client_id}")
        
        # 根据官方API文档直接使用封装好的方法
        print("尝试使用create_spot_limit_order方法创建订单...")
        
        limit_buy_result = await exchange.create_spot_limit_order(
            symbol=symbol,
            side=side,
            price=price,
            quantity=quantity,
            client_id=client_id,
            post_only=True,  # 使用post_only参数
            self_trade_prevention="RejectTaker"  # 添加自成交预防模式
        )
        
        print(f"限价买单创建结果: {limit_buy_result}\n")
        
        # 等待一段时间
        await asyncio.sleep(2)
        
        # 2. 查询订单状态
        order_id = limit_buy_result.get("orderId")
        if order_id:
            print(f"正在查询订单状态: {order_id}")
            order_info = await exchange.get_order(symbol=symbol, order_id=order_id)
            print(f"订单状态: {order_info}\n")
        
        # 3. 取消订单
        if order_id:
            print(f"正在取消订单: {order_id}")
            cancel_result = await exchange.cancel_order(symbol=symbol, order_id=order_id)
            print(f"取消订单结果: {cancel_result}\n")
        
    except Exception as e:
        print(f"出错: {str(e)}")
        import traceback
        traceback.print_exc()


async def perp_order_example():
    """永续合约下单示例"""
    print("======== 永续合约下单示例 ========")
    
    # 初始化交易所API客户端（请替换为您的API密钥）
    api_key = "YOUR_API_KEY"
    api_secret = "YOUR_API_SECRET"
    exchange = BackpackExchange(api_key=api_key, api_secret=api_secret)
    
    try:
        # 1. 创建永续合约限价开多单
        symbol = "BTC_USDT_PERP"  # 永续合约交易对
        side = "Bid"              # 买入，使用Bid
        price = "60000"           # 价格
        quantity = "0.01"         # 数量
        client_id = f"perp_limit_long_{int(time.time())}"  # 客户端订单ID
        
        print(f"正在创建永续合约限价开多单: {symbol}, 价格: {price}, 数量: {quantity}")
        perp_limit_buy_result = await exchange.create_perp_limit_order(
            symbol=symbol,
            side=side,
            price=price,
            quantity=quantity,
            client_id=client_id,
            self_trade_prevention="RejectTaker"  # 添加自成交预防模式
        )
        print(f"永续合约限价开多单创建结果: {perp_limit_buy_result}\n")
        
        # 2. 创建永续合约限价开空单（带止盈止损）
        side = "Ask"             # 卖出，使用Ask
        price = "65000"           # 价格
        client_id = f"perp_limit_short_{int(time.time())}"  # 客户端订单ID
        
        print(f"正在创建永续合约限价开空单: {symbol}, 价格: {price}, 数量: {quantity}")
        perp_limit_sell_result = await exchange.create_perp_limit_order(
            symbol=symbol,
            side=side,
            price=price,
            quantity=quantity,
            client_id=client_id,
            post_only=True,  # 仅挂单不吃单
            take_profit_trigger_price="60000",  # 使用新的止盈参数
            take_profit_limit_price="60100"     # 止盈限价
        )
        print(f"永续合约限价开空单创建结果: {perp_limit_sell_result}\n")
        
        # 3. 创建永续合约市价平多单（卖出，仅减仓）
        side = "Ask"             # 卖出，使用Ask
        client_id = f"perp_market_close_long_{int(time.time())}"  # 客户端订单ID
        
        print(f"正在创建永续合约市价平多单: {symbol}, 数量: {quantity}")
        close_long_result = await exchange.create_perp_market_order(
            symbol=symbol,
            side=side,
            quantity=quantity,
            client_id=client_id,
            reduce_only=True  # 仅减仓
        )
        print(f"永续合约市价平多单创建结果: {close_long_result}\n")
        
        # 4. 创建永续合约市价平空单（买入，仅减仓，使用quote_quantity）
        side = "Bid"              # 买入，使用Bid
        client_id = f"perp_market_close_short_{int(time.time())}"  # 客户端订单ID
        
        print(f"正在创建永续合约市价平空单（使用计价货币数量）: {symbol}")
        close_short_result = await exchange.create_perp_market_order(
            symbol=symbol,
            side=side,
            quote_quantity="650",  # 使用计价货币数量而不是base数量
            client_id=client_id,
            reduce_only=True  # 仅减仓
        )
        print(f"永续合约市价平空单创建结果: {close_short_result}\n")
        
        # 5. 创建带触发价格的永续合约限价单
        side = "Bid"              # 买入，使用Bid
        price = "60000"           # 价格
        trigger_price = "59000"   # 触发价格 
        client_id = f"perp_limit_trigger_{int(time.time())}"  # 客户端订单ID
        
        print(f"正在创建带触发价格的永续合约限价单: {symbol}, 价格: {price}, 触发价格: {trigger_price}")
        trigger_order_result = await exchange.create_perp_limit_order(
            symbol=symbol,
            side=side,
            price=price,
            quantity=quantity,
            client_id=client_id,
            trigger_price=trigger_price,
            trigger_by="LastPrice"  # 使用最新价格作为触发方式
        )
        print(f"带触发价格的永续合约限价单创建结果: {trigger_order_result}\n")
        
    except Exception as e:
        print(f"出错: {str(e)}")


# 主程序
async def main():
    # 运行现货下单示例
    await spot_order_example()
    print("\n")
    
    # 运行永续合约下单示例
    await perp_order_example()


if __name__ == "__main__":
    # 需要Python 3.7+
    asyncio.run(main())
